/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1986-2012 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                 Eclipse Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*          http://www.eclipse.org/org/documents/epl-v10.html           *
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                 Glenn Fowler <gsf@research.att.com>                  *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 * Glenn Fowler
 * AT&T Research
 *
 * convert C prototypes to ANSI, K&R and C++ styles or K&R to ANSI
 * slips into the pp block read
 *
 * define PROTOMAIN for standalone proto
 * PROTOMAIN is coded for minimal library support
 */

#if PROTOMAIN

#include "ppfsm.c"

#include <hashkey.h>

#if PROTO_STANDALONE
#undef	O_RDONLY
#endif

#else

#include "pplib.h"
#include "ppfsm.h"

#endif

#define GENERATED	"/* : : generated by proto : : */\n"

#define PRAGMADIR	"pragma"	/* pragma directive		*/
#define MAGICTOP	80		/* must be in these top lines	*/

#ifndef elementsof
#define elementsof(x)	(sizeof(x)/sizeof(x[0]))
#endif

typedef struct Key_s
{
	const char*	name;
	size_t		size;
	int		hit;
	int		val;
} Key_t;

typedef struct Proto_s			/* proto buffer state		*/
{
	int		brace;		/* {..} level			*/
	int		call;		/* call level			*/
	int		fd;		/* input file descriptor	*/
	char*		file;		/* input file name		*/
	long		flags;		/* coupled flags		*/
	long		options;	/* uncoupled flags		*/
	char*		package;	/* header package		*/
	int		line;		/* input line count		*/
	int		test;		/* testing			*/

	char*		tp;		/* input token base		*/

	int		iz;		/* input buffer size		*/
	char*		ib;		/* input buffer base		*/
	char*		ip;		/* input buffer pointer		*/

	int		oz;		/* output buffer size		*/
	char*		ob;		/* output buffer base		*/
	char*		op;		/* output buffer pointer	*/
	char*		ox;		/* output buffer externalize	*/

	char		cc[3];		/* beg mid end comment char	*/
	char		pushback[4];	/* pushback area for caller	*/

	char		variadic[256];	/* variadic args buffer		*/

	/* output buffer */
	/* slide buffer */
	/* input buffer */
} Proto_t;

/*
 * proto is separate from pp so these undef's are ok
 */

#undef	CLASSIC
#define CLASSIC		(1L<<0)
#undef	DECLARE
#define DECLARE		(1L<<1)
#undef	DEFINE
#define DEFINE		(1L<<2)
#undef	DIRECTIVE
#define DIRECTIVE	(1L<<3)
#undef	ERROR
#define ERROR		(1L<<4)
#undef	EXTERN
#define EXTERN		(1L<<5)
#undef	EXTERNALIZE
#define EXTERNALIZE	(1L<<6)
#undef	IDID
#define IDID		(1L<<7)
#undef	INDIRECT
#define INDIRECT	(1L<<8)
#undef	INIT
#define INIT		(1L<<9)
#undef	INIT_DEFINE
#define INIT_DEFINE	(1L<<10)
#undef	INIT_INCLUDE
#define INIT_INCLUDE	(1L<<11)
#undef	JUNK
#define JUNK		(1L<<12)
#undef	LINESYNC
#define LINESYNC	(1L<<13)
#undef	MANGLE
#define MANGLE		(1L<<14)
#undef	MATCH
#define MATCH		(1L<<15)
#undef	MORE
#define MORE		(1L<<16)
#undef	OTHER
#define OTHER		(1L<<17)
#undef	PASS
#define PASS		(1L<<18)
#undef	PLUSONLY
#define PLUSONLY	(1L<<19)
#undef	PLUSPLUS
#define PLUSPLUS	(1L<<20)
#undef	RECURSIVE
#define RECURSIVE	(1L<<21)
#undef	SHARP
#define SHARP		(1L<<22)
#undef	SKIP
#define SKIP		(1L<<23)
#undef	SLIDE
#define SLIDE		(1L<<24)
#undef	TOKENS
#define TOKENS		(1L<<25)
#undef	TYPEDEF
#define TYPEDEF		(1L<<26)
#undef	VARIADIC
#define VARIADIC	(1L<<27)
#undef	VARIADIC2
#define VARIADIC2	(1L<<28)
#undef	YACC
#define YACC		(1L<<29)
#undef	YACCSPLIT
#define YACCSPLIT	(1L<<30)
#undef	YACC2
#define YACC2		(1L<<31)

#undef	GLOBAL
#define GLOBAL		(MORE)

#undef	REGULAR
#define REGULAR		(1L<<0)

#ifndef CHUNK
#define CHUNK		1024
#endif
#define BLOCK		(16*CHUNK)

#define T_VA_START	(N_TOKEN+1)

#define RESERVED(b,e,n)	((((long)(b))<<16)|(((long)(e))<<8)|((long)(n)))

#define KEYENT(s,m,v)	{s,sizeof(s)-1,m,v}

#define HIT_prototyped	0x01
#define HIT_noticed	0x02

static const Key_t	pragmas[] =
{
	KEYENT("prototyped",	HIT_prototyped,	1),	/* NOTE: first entry */
	KEYENT("noprototyped",	HIT_prototyped,	0),
	KEYENT("noticed",	HIT_noticed,	1),
	KEYENT("nonoticed",	HIT_noticed,	0),
};

#if PROTOMAIN
static const Key_t	notices[] =
{
	KEYENT("Copyright",	HIT_noticed,	1),
	KEYENT("COPYRIGHT",	HIT_noticed,	1),
	KEYENT("copyright",	HIT_noticed,	1),
	KEYENT("Public Domain",	HIT_noticed,	0),
	KEYENT("PUBLIC DOMAIN",	HIT_noticed,	0),
};
#endif

/*
 * generate integer
 * pointer to end returned
 */

static char*
number(register char* p, register long n)
{
	register long	d;

	for (d = 1000000; d > 1; d /= 10)
		if (n >= d) *p++ = '0' + (n / d) % 10;
	*p++ = '0' + n % 10;
	return p;
}

#if PROTOMAIN

static int		errors;

#if PROTO_STANDALONE

/*
 * namespace pollution forces us to claim parts of libc
 */

#undef	memcpy
#define memcpy(t,f,n)	memcopy(t,f,n)
#undef	strcpy
#define strcpy(t,f)	strcopy(t,f)
#undef	strlen
#define strlen(s)	sstrlen(s)
#undef	strncmp
#define strncmp(s,t,n)	sstrncmp(s,t,n)

/*
 * environmentally safe strlen()
 */

static int
sstrlen(register const char* s)
{
	register const char*	b;

	for (b = s; *s; s++);
	return s - b;
}

/*
 * environmentally safe strncmp()
 */

static int
sstrncmp(register const char* s, register const char* t, register int n)
{
	register const char*	e = s + n;

	while (s < e)
	{
		if (*s != *t || !*s)
			return *s - *t;
		s++;
		t++;
	}
	return 0;
}

/*
 * strcpy() except pointer to end returned
 */

static char*
strcopy(register char* s, register const char* t)
{
	while (*s++ = *t++);
	return s - 1;
}

#endif

static void
proto_error(char* iob, int level, char* msg, char* arg)
{
	register char*	p;
	char		buf[1024];

	p = strcopy(buf, "proto: ");
	if (iob)
	{
		register Proto_t*	proto = (Proto_t*)(iob - sizeof(Proto_t));

		if (proto->line)
		{
			if (proto->file)
			{
				*p++ = '"';
				p = strcopy(p, proto->file);
				*p++ = '"';
				*p++ = ',';
				*p++ = ' ';
			}
			p = strcopy(p, "line ");
			p = number(p, proto->line);
		}
		else if (proto->file)
			p = strcopy(p, proto->file);
	}
	else
	{
		p = strcopy(p, msg);
		msg = arg;
		arg = 0;
	}
	if (*(p - 1) != ' ')
	{
		*p++ = ':';
		*p++ = ' ';
	}
	if (level == 1)
		p = strcopy(p, "warning: ");
	p = strcopy(p, msg);
	if (arg)
	{
		*p++ = ' ';
		p = strcopy(p, arg);
	}
	*p++ = '\n';
	write(2, buf, p - buf);
	if (level >= 3)
		exit(level - 2);
	if (level >= 2)
		errors++;
}

/*
 * memcpy() but pointer to end returned
 */

static char*
memcopy(register char* s, register char* t, int n)
{
	register char*	e = t + n;

	while (t < e) *s++ = *t++;
	return s;
}

#include "../libast/port/astlicense.c"

#else

#define memcopy(s,t,n)	(((char*)memcpy(s,t,n))+(n))

#endif

/*
 * generate line sync
 * pointer to end returned
 */

static char*
linesync(register Proto_t* proto, register char* p, register long n)
{
#if PROTOMAIN
	if (proto->flags & LINESYNC)
#endif
	{
#if PROTOMAIN
		p = strcopy(p, "\n#line ");
#else
		p = strcopy(p, "\n# ");
#endif
		p = number(p, n);
		*p++ = '\n';
	}
	return p;
}

/*
 * output init header
 * pointer to end returned
 */

static char*
init(Proto_t* proto, char* op, int flags)
{
	register char*	s;

	if (flags & INIT_DEFINE)
	{
		op = strcopy(op, "\
\n\
#if !defined(__PROTO__)\n\
#  if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)\n\
#    if defined(__cplusplus)\n\
#      define __LINKAGE__	\"C\"\n\
#    else\n\
#      define __LINKAGE__\n\
#    endif\n\
#    define __STDARG__\n\
#    define __PROTO__(x)	x\n\
#    define __OTORP__(x)\n\
#    define __PARAM__(n,o)	n\n\
#    if !defined(__STDC__) && !defined(__cplusplus)\n\
#      if !defined(c_plusplus)\n\
#      	define const\n\
#      endif\n\
#      define signed\n\
#      define void		int\n\
#      define volatile\n\
#      define __V_		char\n\
#    else\n\
#      define __V_		void\n\
#    endif\n\
#  else\n\
#    define __PROTO__(x)	()\n\
#    define __OTORP__(x)	x\n\
#    define __PARAM__(n,o)	o\n\
#    define __LINKAGE__\n\
#    define __V_		char\n\
#    define const\n\
#    define signed\n\
#    define void		int\n\
#    define volatile\n\
#  endif\n\
#  define __MANGLE__	__LINKAGE__\n\
#  if defined(__cplusplus) || defined(c_plusplus)\n\
#    define __VARARG__	...\n\
#  else\n\
#    define __VARARG__\n\
#  endif\n\
#  if defined(__STDARG__)\n\
#    define __VA_START__(p,a)	va_start(p,a)\n\
#  else\n\
#    define __VA_START__(p,a)	va_start(p)\n\
#  endif\n\
#  if !defined(__INLINE__)\n\
#    if defined(__cplusplus)\n\
#      define __INLINE__	extern __MANGLE__ inline\n\
#    else\n\
#      if defined(_WIN32) && !defined(__GNUC__)\n\
#      	define __INLINE__	__inline\n\
#      endif\n\
#    endif\n\
#  endif\n\
#endif\n\
#if !defined(__LINKAGE__)\n\
#define __LINKAGE__		/* 2004-08-11 transition */\n\
#endif\n\
");
	}
	else
		op = strcopy(op, "\
\n\
#if !defined(__PROTO__)\n\
#include <prototyped.h>\n\
#endif\n\
#if !defined(__LINKAGE__)\n\
#define __LINKAGE__		/* 2004-08-11 transition */\n\
#endif\n\
");
	if (proto->package)
	{
		s = "\
#ifndef	__MANGLE_%_DATA__\n\
#  ifdef _BLD_%\n\
#    ifdef __EXPORT__\n\
#      define	__MANGLE_%_DATA__	__MANGLE__ __EXPORT__\n\
#    else\n\
#      define	__MANGLE_%_DATA__	__MANGLE__\n\
#    endif\n\
#    define	__MANGLE_%_FUNC__	__MANGLE__\n\
#  else\n\
#    ifdef __IMPORT__\n\
#      define	__MANGLE_%_DATA__	__MANGLE__ __IMPORT__\n\
#    else\n\
#      define	__MANGLE_%_DATA__	__MANGLE__\n\
#    endif\n\
#    define	__MANGLE_%_FUNC__	__MANGLE__\n\
#  endif\n\
#endif\n\
";
		for (;;)
		{
			switch (*op++ = *s++)
			{
			case 0:
				op--;
				break;
			case '%':
				op = strcopy(op - 1, proto->package);
				continue;
			default:
				continue;
			}
			break;
		}
	}
	return op;
}

#define BACKOUT()	(op=ko)
#define CACHE()		do{CACHEIN();CACHEOUT();call=proto->call;}while(0)
#define CACHEIN()	(ip=proto->ip)
#define CACHEOUT()	(op=proto->op)
#define GETCHR()	(*(unsigned char*)ip++)
#define KEEPOUT()	(ko=op)
#define LASTOUT()	(*(op-1))
#define PUTCHR(c)	(*op++=(c))
#define SYNC()		do{SYNCIN();SYNCOUT();proto->flags&=~(EXTERN|INIT|OTHER|VARIADIC|VARIADIC2);proto->flags|=flags&(EXTERN|INIT|OTHER|VARIADIC|VARIADIC2);proto->call=call;}while(0)
#define SYNCIN()	(proto->ip=ip)
#define SYNCOUT()	(proto->op=op)
#define UNGETCHR()	(ip--)
#define UNPUTCHR()	(op--)

/*
 * advance to the next non-space character
 */

static char*
nns(register char* s)
{
	while (*s == ' ' || *s == '\t' || *s == '\n')
		s++;
	return s;
}

#define DIR_if	01
#define DIR_el	02
#define DIR_en	03
#define DIR	03

/*
 * update directive mask
 */

static int
directive(register char* s, int dir)
{
	switch (*(s = nns(s)))
	{
	case 'e':
	case 'i':
		dir <<= 2;
		switch (*++s)
		{
		case 'f':
			dir |= DIR_if;
			break;
		case 'l':
			dir |= DIR_el;
			break;
		case 'n':
			dir |= DIR_en;
			break;
		}
		break;
	}
	return dir;
}

/*
 * the tokenizer
 * top level calls loop until EOB
 * recursive calls just return the next token
 */

static int
lex(register Proto_t* proto, register long flags)
{
	register char*		ip;
	register char*		op;
	register int		c;
	register int		state;
	register short*		rp;
	char*			m;
	char*			e;
	char*			t;
	char*			bp;
	char*			v;
	char*			im;
	char*			ko;
	char*			aom;
	int			n;
	int			line;
	int			quot;
	int			brack;
	int			sub;
	int			x;
	int			vc;

	char*			ie = 0;
	char*			om = 0;
	char*			aim = 0;
	char*			aie = 0;
	char*			func = 0;
	int			call = 0;
	int			dir = 0;
	int			group = 0;
	int			last = 0;
	int			paren = 0;
#if PROTOMAIN
	char*			qe = 0;
	int			qn = 0;
	int			args = 0;
#endif

	CACHE();
#if PROTOMAIN
	if (flags & EXTERN) KEEPOUT();
#endif
 fsm_start:
	proto->tp = ip;
	state = PROTO;
	bp = ip;
	do
	{
		rp = fsm[state];
 fsm_get:
		while (!(state = rp[c = GETCHR()]));
 fsm_next:
		;
	} while (state > 0);
	if ((n = ip - bp - 1) > 0)
	{
		ip = bp;
		MEMCPY(op, ip, n);
		ip++;
	}
	state = ~state;
 fsm_terminal:
	switch (TERM(state))
	{
	case S_CHR:
		if (op > proto->ob && *(op - 1) == '=' && (op == proto->ob + 1 || *(op - 2) != '=')) switch (c)
		{
		case '+':
		case '-':
		case '*':
		case '&':
			PUTCHR(' ');
			break;
		}
		PUTCHR(c);
		break;

	case S_CHRB:
		UNGETCHR();
		c = LASTOUT();
		break;

	case S_COMMENT:
		switch (c)
		{
		case '\n':
			if (INCOMMENTXX(rp)) goto fsm_newline;
			PUTCHR(c);
			proto->line++;
			rp = fsm[COM2];
			break;
		case '/':
#if PROTOMAIN
			if ((flags & (EXTERN|MATCH)) == EXTERN) BACKOUT();
			else
#endif
			PUTCHR(c);
			if (INCOMMENTXX(rp))
			{
				rp = fsm[COM5];
				break;
			}
			goto fsm_start;
		case EOF:
			break;
		default:
#if PROTOMAIN
			if ((flags & (EXTERN|MATCH)) == EXTERN) BACKOUT();
			else
#endif
			PUTCHR(c);
			rp = fsm[INCOMMENTXX(rp) ? COM5 : COM3];
			break;
		}
		bp = ip;
		goto fsm_get;

	case S_EOB:
		if (c)
		{
			if (state = fsm[TERMINAL][INDEX(rp)+1])
				goto fsm_terminal;
			SYNC();
			return 0;
		}
		UNGETCHR();
 fsm_eob:
		if ((flags & (DECLARE|GLOBAL|RECURSIVE)) == GLOBAL && (proto->flags & MORE))
		{
#if PROTOMAIN
			if (!(flags & EXTERN)) /* XXX */
#endif
			flags |= SLIDE;
			c = ip - proto->ib;
			if (!(flags & MATCH))
				im = proto->tp;
			if (ip > proto->ib)
			{
				n = ip - im;
				if (ip - n < proto->ib)
					proto->flags |= ERROR;
				memcopy(proto->ib - n, ip - n, n);
				ip = proto->ib;
			}
			proto->tp -= c;
			if (flags & MATCH)
			{
				im -= c;
				ie -= c;
			}
			if (aim)
				aim -= c;
			if (aie)
				aie -= c;
			if ((n = read(proto->fd, ip, proto->iz)) > 0)
			{
				if ((proto->options & REGULAR) && n < proto->iz)
				{
					proto->flags &= ~MORE;
					close(proto->fd);
				}
				*(ip + n) = 0;
				if (state & SPLICE)
					goto fsm_splice;
				bp = ip;
				goto fsm_get;
			}
			*ip = 0;
			proto->flags &= ~MORE;
			close(proto->fd);
		}
		if (state & SPLICE)
			goto fsm_splice;
		/* NOTE: RECURSIVE lex() should really SLIDE too */
		if (!(flags & RECURSIVE) && (state = rp[c = EOF]))
		{
			bp = ip;
			goto fsm_next;
		}
		SYNC();
		return 0;

	case S_LITBEG:
		quot = c;
#if PROTOMAIN
		if (c == '"' && qe)
		{
			for (n = 0, t = qe + 1; t < op && (*t == ' ' || *t == '\t' || *t == '\n' && ++n || *t >= 'A' && *t <= 'Z' || *t == '_'); t++);
			if (t == op)
			{
				op = qe;
				qe = 0;
				qn = n;
			}
			else PUTCHR(c);
		}
		else
#endif
		PUTCHR(c);
		rp = fsm[LIT1];
		bp = ip;
		goto fsm_get;

	case S_LITEND:
		if (c == quot)
		{
#if PROTOMAIN
			if (!(flags & DIRECTIVE))
				qe = (c == '"') ? op : (char*)0;
#endif
			PUTCHR(c);
#if PROTOMAIN
			while (qn > 0)
			{
				qn--;
				PUTCHR('\n');
			}
#endif
		}
		else if (c != '\n' && c != EOF)
		{
			PUTCHR(c);
			bp = ip;
			goto fsm_get;
		}
		else
		{
#if PROTOMAIN
			while (qn > 0)
			{
				qn--;
				PUTCHR('\n');
			}
#endif
			UNGETCHR();
		}
		c = T_INVALID;
		break;

	case S_LITESC:
#if PROTOMAIN
		if (flags & CLASSIC) PUTCHR(c);
		else
#endif
		switch (c)
		{
		case 'a':
			n = CC_bel;
			goto fsm_oct;
		case 'E':
			n = CC_esc;
			goto fsm_oct;
		case 'v':
			n = CC_vt;
			goto fsm_oct;
		case 'x':
			SYNC();
			lex(proto, (flags & GLOBAL) | RECURSIVE);
			for (n = x = 0; (c = GETCHR()), x < 3; x++) switch (c)
			{
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7':
			case '8': case '9':
				n = (n << 4) + c - '0';
				break;
			case 'a': case 'b': case 'c': case 'd':
			case 'e': case 'f':
				n = (n << 4) + c - 'a' + 10;
				break;
			case 'A': case 'B': case 'C': case 'D':
			case 'E': case 'F':
				n = (n << 4) + c - 'A' + 10;
				break;
			default:
				goto fsm_hex;
			}
 fsm_hex:
			UNGETCHR();
 fsm_oct:
			PUTCHR(((n >> 6) & 07) + '0');
			PUTCHR(((n >> 3) & 07) + '0');
			PUTCHR((n & 07) + '0');
			break;
		default:
			PUTCHR(c);
			break;
		}
		rp = fsm[LIT1];
		bp = ip;
		goto fsm_get;

	case S_MACRO:
		UNGETCHR();
#if PROTOMAIN
		if ((flags & EXTERN) && *proto->tp == 's' && !strncmp(proto->tp, "static", 6))
		{
			c = T_EXTERN;
			break;
		}
#endif
		if (*proto->tp == '_' && !strncmp(proto->tp, "__STDPP__directive", 6)) c = '#';
		else c = T_ID;

		break;

	case S_NL:
 fsm_newline:
		proto->line++;
#if PROTOMAIN
		if (flags & EXTERN)
		{
			if (op != proto->ob && LASTOUT() != ' ' && LASTOUT() != '\n')
				PUTCHR(' ');
		}
		else
#endif
		PUTCHR(c);
		if (flags & DIRECTIVE)
		{
#if PROTOMAIN
			if (flags & CLASSIC)
			{
				if (flags & EXTERN) BACKOUT();
				if (flags & JUNK)
				{
					*(ip - 1) = 0;
					op = strcopy(om, "/* ");
					op = strcopy(op, im);
					op = strcopy(op, " */\n");
				}
				flags &= ~(DEFINE|DIRECTIVE|IDID|INDIRECT|JUNK|MATCH|SHARP|TYPEDEF);
			}
			else
#endif
			{
				if ((flags & (DEFINE|SHARP)) == (DEFINE|SHARP))
				{
					*(ip - 1) = 0;
					op = strcopy(om, "#if defined(__STDC__) || defined(__STDPP__)\n");
					op = strcopy(op, im);
					op = strcopy(op, "\n#else\n");
					bp = ip;
					ip = im;
					*op++ = *ip++;
					while (*op = *ip++)
						if (*op++ == '#' && *ip != '(')
						{
							op--;
							while (*--op == ' ' || *op == '\t');
							if (*ip == '#')
							{
								op = strcopy(op + 1, "/**/");
								while (*++ip == ' ' || *ip == '\t');
							}
							else
							{
								if (*op != '"') *++op = '"';
								op++;
								while (*ip == ' ' || *ip == '\t') ip++;
								while ((c = *ip) >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_') *op++ = *ip++;
								while (*ip == ' ' || *ip == '\t') ip++;
								if (*ip == '"') ip++;
								else *op++ = '"';
							}
						}
					ip = bp;
					op = strcopy(op, "\n#endif\n");
					op = linesync(proto, op, proto->line);
				}
				flags &= ~(DEFINE|DIRECTIVE|IDID|INDIRECT|MATCH|OTHER|SHARP|SKIP|TOKENS|TYPEDEF);
			}
			call = 0;
			group = 0;
			paren = 0;
			last = '\n';
		}
		if (paren == 0 && (flags & (MATCH|RECURSIVE|SKIP|SLIDE)) == SLIDE)
		{
#if PROTOMAIN
			if (flags & EXTERN) BACKOUT();
#endif
			SYNC();
			return 0;
		}
		goto fsm_start;

	case S_QUAL:
		PUTCHR(c);
		rp = fsm[NEXT(state)];
		bp = ip;
		goto fsm_get;

	case S_TOK:
		PUTCHR(c);
		c = TYPE(state);
		break;

	case S_TOKB:
		UNGETCHR();
		c = TYPE(state);
		break;

	case S_RESERVED:
		UNGETCHR();
		c = T_ID;
		if (!(flags & DECLARE)) switch (RESERVED(*proto->tp, *(ip - 1), ip - proto->tp))
		{
		case RESERVED('N', 'N', 3):
			if (proto->tp[1] == 'o')
				c = T_DO;
			break;
		case RESERVED('d', 'o', 2):
			c = T_DO;
			break;
		case RESERVED('e', 'e', 4):
			if (!(flags & RECURSIVE) && (flags & (DIRECTIVE|TOKENS)) != DIRECTIVE && !strncmp(proto->tp, "else", 4))
			{
				c = T_ELSE;
				goto fsm_id;
			}
			break;
		case RESERVED('e', 'n', 6):
			if (!strncmp(proto->tp, "extern", 6))
				c = T_EXTERN;
			break;
		case RESERVED('f', 'r', 3):
			if (!(flags & RECURSIVE) && !strncmp(proto->tp, "for", 3))
			{
				c = T_FOR;
				goto fsm_id;
			}
			break;
		case RESERVED('i', 'f', 2):
			c = T_IF;
			break;
		case RESERVED('i', 'e', 6):
			if (!strncmp(proto->tp, "inline", 6) && !(flags & (MATCH|SKIP|TOKENS|TYPEDEF)) && proto->brace == 0 && paren == 0 && group == 0 && (last == ';' || last == '}' || last == '\n' || last == 0))
			{
				flags |= SKIP;
				SYNC();
				line = proto->line;
				op = strcopy(op - 6, "__INLINE__");
				SYNC();
			}
			break;
		case RESERVED('r', 'n', 6):
			if (!(flags & RECURSIVE) && !strncmp(proto->tp, "return", 6))
			{
				c = T_RETURN;
				goto fsm_id;
			}
			break;
		case RESERVED('s', 'c', 6):
			if ((proto->options & EXTERNALIZE) && !strncmp(proto->tp, "static", 6))
			{
				proto->ox = op - 6;
				flags |= EXTERNALIZE;
			}
			break;
		case RESERVED('t', 'f', 7):
			if (!(flags & RECURSIVE) && !strncmp(proto->tp, "typedef", 7))
			{
				flags |= TYPEDEF;
				c = T_EXTERN;
			}
			break;
		case RESERVED('v', 't', 8):
			if (*ip == '(' && !strncmp(proto->tp, "va_start", 8)) c = T_VA_START;
			break;
		case RESERVED('v', 'd', 4):
			if (!strncmp(proto->tp, "void", 4))
			{
				if (flags & (CLASSIC|PLUSONLY|INIT_DEFINE|INIT_INCLUDE)) c = T_VOID;
				else
				{
					SYNC();
					line = proto->line;
					if (lex(proto, (flags & GLOBAL) | RECURSIVE) == '*')
					{
						memcopy(op - 4, "__V_", 4);
						memcopy(ip - 4, "__V_", 4);
					}
					else c = T_VOID;
					proto->line = line;
					SYNC();
					bp = ip;
				}
			}
			break;
		case RESERVED('w', 'e', 5):
			if (!(flags & RECURSIVE) && !strncmp(proto->tp, "while", 5))
			{
				c = T_WHILE;
				goto fsm_id;
			}
			break;
		}
#if PROTOMAIN
		if ((flags & CLASSIC) && c != T_EXTERN)
			c = T_ID;
#endif
		break;

	case S_VS:
		goto fsm_start;

	case S_WS:
		UNGETCHR();
#if PROTOMAIN
		if ((flags & (EXTERN|MATCH)) == EXTERN)
		{
			while (op > proto->ob && (*(op - 1) == ' ' || *(op - 1) == '\t'))
				op--;
			if (op > proto->ob && *(op - 1) != '\n') *op++ = ' ';
		}
#endif
		goto fsm_start;

	default:
		if (state & SPLICE)
		{
			if (c == '\\')
			{
				if (!(n = GETCHR()))
				{
					goto fsm_eob;
 fsm_splice:
					c = '\\';
					n = GETCHR();
				}
				if (n == '\n')
				{
					proto->line++;
					PUTCHR('\\');
					PUTCHR('\n');
					bp = ip;
					goto fsm_get;
				}
				UNGETCHR();
			}
			state &= ~SPLICE;
			if (state >= TERMINAL)
				goto fsm_terminal;
			rp = fsm[state];
		}
		PUTCHR(c);
		bp = ip;
		goto fsm_get;
	}
	if (!(flags & (INIT_DEFINE|INIT_INCLUDE|RECURSIVE)))
	{
		if (!(flags & DIRECTIVE)) switch (c)
		{
		case '(':
#if PROTOMAIN
			if (!(flags & CLASSIC) || proto->brace == 0)
#endif
			{
				if (paren++ == 0)
				{
#if PROTOMAIN
					if (!(flags & CLASSIC) || group <= 1)
#endif
					{
#if PROTOMAIN
						args = 0;
#endif
						if (group++ == 0) group++;
						else if (flags & INDIRECT) call++;
						flags |= MATCH;
						im = ip - 1;
						om = op - 1;
					}
					sub = 0;
				}
				else if (paren == 2 && !aim)
				{
					sub++;
					if (last == '(')
					{
						flags &= ~MATCH;
						om = 0;
					}
					else if (flags & INDIRECT)
					{
						aim = ip - 1;
						aom = op - 1;
					}
					else if ((flags & (MATCH|TOKENS)) == MATCH)
					{
						for (m = ip - 2; m > im && (*m == ' ' || *m == '\t'); m--);
						if (m != im && sub == 1)
						{
							m = im + (*nns(ip) == '*');
						}
						if (m == im)
						{
							flags &= ~MATCH;
							om = 0;
						}
					}
					else if ((flags & MATCH) && sub == 1 && *nns(ip) != '*')
					{
						flags &= ~MATCH;
						om = 0;
					}
				}
				flags &= ~TOKENS;
			}
			break;
		case ')':
#if PROTOMAIN
			if (!(flags & CLASSIC) || proto->brace == 0)
#endif
			if (--paren == 0)
			{
#if PROTOMAIN
				if (flags & CLASSIC)
				{
					if (group != 2)
					{
						c = T_ID;
						break;
					}
					group++;
				}
#endif
				ie = ip;
			}
			else if (paren == 1 && (flags & INDIRECT) && !aie)
				aie = ip;
			break;
		case '*':
			if (last == '(' && group == 2)
			{
				group--;
				if (paren == 1)
				{
					flags |= INDIRECT;
					aim = aie = 0;
				}
			}
			break;
		case '#':
			dir = directive(ip, dir);
			if (proto->brace == 0 && paren == 0 && last != '=' && (flags & (CLASSIC|DECLARE|DIRECTIVE|MATCH|PLUSONLY|SKIP|TOKENS)) == (MATCH|TOKENS) && ((dir & DIR) != DIR_en || ((dir>>2) & DIR) != DIR_if))
				flags |= DIRECTIVE;
			else if (!(flags & (DECLARE|DIRECTIVE)))
			{
				flags |= DIRECTIVE;
				if (!(flags & PLUSONLY))
				{
					bp = ip;
					while (*ip == ' ' || *ip == '\t') ip++;
					if (*ip == 'l' && *++ip == 'i' && *++ip == 'n' && *++ip == 'e')
					{
						if (*++ip == ' ' || *ip == '\t')
						{
							proto->line = 0;
							while (*++ip >= '0' && *ip <= '9')
								proto->line = proto->line * 10 + *ip - '0';
							proto->line--;
						}
					}
#if PROTOMAIN
					else if ((flags & (CLASSIC|EXTERN)) == CLASSIC)
					{
						n = 0;
						t = ip + 6;
						while (ip < t && *ip >= 'a' && *ip <= 'z')
							n = HASHKEYPART(n, *ip++);
						switch (n)
						{
						case HASHKEY4('e','l','s','e'):
						case HASHKEY5('e','n','d','i','f'):
							while (*ip == ' ' || *ip == '\t') ip++;
							if (*ip != '\n' && *ip != '/' && *(ip + 1) != '*')
							{
								flags |= JUNK|MATCH;
								im = ip;
								om = op + (ip - bp);
							}
							break;
						case HASHKEY4('e','l','i','f'):
						case HASHKEY5('e','r','r','o','r'):
						case HASHKEY2('i','f'):
						case HASHKEY5('i','f','d','e','f'):
						case HASHKEY6('i','f','n','d','e','f'):
						case HASHKEY5('u','n','d','e','f'):
							break;
						case HASHKEY6('i','n','c','l','u','d'):
							if (*ip == 'e') ip++;
							/*FALLTHROUGH*/
						case HASHKEY6('d','e','f','i','n','e'):
						case HASHKEY6('p','r','a','g','m','a'):
							if (*ip < 'a' || *ip > 'z') break;
							/*FALLTHROUGH*/
						default:
							flags |= JUNK|MATCH;
							im = bp - 1;
							om = op - 1;
							break;
						}
					}
					else
#endif
					{
						if (*ip == 'i' && *++ip == 'n' && *++ip == 'c' && *++ip == 'l' && *++ip == 'u' && *++ip == 'd' && *++ip == 'e')
						{
							while (*++ip == ' ' || *ip == '\t');
							if (*ip++ == '<' && *ip++ == 's' && *ip++ == 't' && *ip++ == 'd' && *ip++ == 'a' && *ip++ == 'r' && *ip++ == 'g' && *ip++ == '.' && *ip++ == 'h' && *ip++ == '>')
							{
								op = strcopy(op, "\
if !defined(va_start)\n\
#if defined(__STDARG__)\n\
#include <stdarg.h>\n\
#else\n\
#include <varargs.h>\n\
#endif\n\
#endif\n\
");
								op = linesync(proto, op, proto->line);
								break;
							}
						}
						else if (*ip == 'd' && *++ip == 'e' && *++ ip == 'f' && *++ip == 'i' && *++ip == 'n' && *++ip == 'e' && (*++ip == ' ' || *ip == '\t'))
						{
							while (*++ip == ' ' || *ip == '\t');
							if (*ip == 'e' && *++ip == 'x' && *++ ip == 't' && *++ip == 'e' && *++ip == 'r' && *++ip == 'n' && (*++ip == ' ' || *ip == '\t'))
							{
								t = ip;
								while (*++t == ' ' || *t == '\t');
								if (*t == 'e' && *++t == 'x' && *++ t == 't' && *++t == 'e' && *++t == 'r' && *++t == 'n' && (*++t == ' ' || *t == '\t' || *t == '\n' || *t == '\r'))
									ip = t;
								t = ip;
								while (*++t == ' ' || *t == '\t');
								if (*t == '_' && *(t + 1) == '_')
								{
									op = strcopy(op, "undef __MANGLE__\n");
									op = linesync(proto, op, proto->line);
									op = strcopy(op, "#define __MANGLE__ __LINKAGE__");
									break;
								}
							}
							flags |= DEFINE|MATCH;
							im = bp - 1;
							om = op - 1;
						}
						else if (*ip == 'u' && *++ip == 'n' && *++ ip == 'd' && *++ip == 'e' && *++ip == 'f' && (*++ip == ' ' || *ip == '\t'))
						{
							while (*++ip == ' ' || *ip == '\t');
							if (*ip == 'e' && *++ip == 'x' && *++ ip == 't' && *++ip == 'e' && *++ip == 'r' && *++ip == 'n' && (*++ip == ' ' || *ip == '\t' || *ip == '\n' || *ip == '\r'))
							{
								op = strcopy(op, "undef __MANGLE__\n");
								op = linesync(proto, op, proto->line);
								op = strcopy(op, "#define __MANGLE__ __LINKAGE__");
								break;
							}
							flags |= DEFINE|MATCH;
							im = bp - 1;
							om = op - 1;
						}
					}
					ip = bp;
				}
				break;
			}
			else
				break;
			/*FALLTHROUGH*/
		case '{':
			if (proto->brace++ == 0 && paren == 0)
			{
				if (last == '=') flags |= INIT;
#if PROTOMAIN
				else if (flags & CLASSIC)
				{
					if ((flags & (MATCH|OTHER|SKIP)) == MATCH)
					{
						if (args)
						{
							v = number(op, args < 0 ? -args : args);
							v = strcopy(v, " argument actual/formal mismatch");
							*v++ = ' ';
							v = memcopy(v, im, ie - im);
							*v = 0;
							proto_error((char*)proto + sizeof(Proto_t), 2, op, NiL);
						}
						ip--;
						/*UNDENT...*/
	v = ie;
	while (ie < ip)
		if (*ie++ == '/' && *ie == '*')
		{
			e = ie - 1;
			while (++ie < ip)
			{
				if (*ie == '*')
				{
					while (ie < ip && *ie == '*') ie++;
					if (ie < ip && *ie == '/')
					{
						while (++ie < ip && (*ie == ' ' || *ie == '\t'));
						while (e > v && (*(e - 1) == ' ' || *(e - 1) == '\t')) e--;
						if (e > v && *e != '\n') *e++ = ' ';
						t = ie;
						while (--e >= v)
							*--t = *e;
						v = t;
						break;
					}
				}
			}
		}
	ie = v;
						/*...INDENT*/
						op = om++;
						if (flags & EXTERN)
						{
							v = op;
							while (v > ko && *--v != ' ');
							if (*v != ' ')
							{
								om = (v = (op += 4)) + 1;
								while (v >= ko + 4)
								{
									*v = *(v - 4);
									v--;
								}
								memcopy(ko, "int ", 4);
							}
							if (*v == ' ')
							{
								while (*(v + 1) == '*')
									*v++ = '*';
								*v = '\t';
								if ((v - ko) <= 8)
								{
									om = (e = ++op) + 1;
									while (e > v)
									{
										*e = *(e - 1);
										e--;
									}
								}
							}
							om = (v = (op += 7)) + 1;
							while (v >= ko + 7)
							{
								*v = *(v - 7);
								v--;
							}
							memcopy(ko, "extern ", 7);
						}
						PUTCHR('(');
						t = op;
						e = 0;
						/*UNDENT...*/
	while (ie < ip)
	{
		if ((c = *ie) == ' ' || c == '\t' || c == '\n')
		{
			while ((c = *++ie) == ' ' || c == '\t' || c == '\n');
			if (ie >= ip) break;
			if (c != '*' && op > om) PUTCHR(' ');
		}
		if ((n = ((c = *ie) == ',')) || c == ';')
		{
			if (flags & EXTERN)
			{
				m = op;
				while (op > om && ((c = *(op - 1)) == '(' || c == ')' || c == '[' || c == ']'))
					op--;
				v = op;
				while (op > om && (c = *(op - 1)) != ' ' && c != '*')
					op--;
				while (*(op - 1) == ' ')
					op--;
				if (!e)
				{
					e = op;
					while (e > om && *(e - 1) == '*')
						e--;
				}
#if _s5r4_386_compiler_bug_fixed_
				if (op <= om || *(op - 1) == ',' && (*op++ = ' '))
					op = strcopy(op, "int");
#else
				if (op <= om)
					op = strcopy(op, "int");
				else if (*(op - 1) == ',')
					op = strcopy(op, " int");
#endif
				while (v < m)
					PUTCHR(*v++);
			}
			PUTCHR(',');
			if (n)
			{
				if (x = !e) e = op - 1;
				PUTCHR(' ');
				m = t;
				while (m < e)
					PUTCHR(*m++);
				if (x)
				{
					m = e;
					while (*--e != ' ');
					while (*(e - 1) == '*') e--;
					op -= m - e;
				}
			}
			while ((c = *++ie) == ' ' || c == '\t' || c == '\n');
			if (ie >= ip) UNPUTCHR();
			else PUTCHR(' ');
			if (!n)
			{
				t = op;
				e = 0;
			}
		}
		else if (*ie == '*')
		{
			if (op > om && (c = *(op - 1)) == ' ') op--;
			while (*ie == '*') PUTCHR(*ie++);
			while (*ie == ' ' || *ie == '\t' || *ie == '\n') ie++;
			if (c != '(') PUTCHR(' ');
		}
		else if (*ie == '(')
		{
			if (op > om && *(op - 1) == ' ') op--;
			PUTCHR(*ie++);
			while (*ie == ' ' || *ie == '\t' || *ie == '\n') ie++;
		}
		else if (*ie == ')')
		{
			if (op > om && *(op - 1) == '(')
				proto_error((char*)proto + sizeof(Proto_t), 1, "function pointer argument prototype omitted", NiL);
			PUTCHR(*ie++);
			while (*ie == ' ' || *ie == '\t' || *ie == '\n') ie++;
		}
		else if ((flags & EXTERN) && (op == om || *(op - 1) == ' ') && *ie == 'r' && !strncmp(ie, "register", 8) && (*(ie + 8) == ' ' || *(ie + 8) == '\t' || *(ie + 8) == '\n'))
		{
			ie += 8;
			if (op > om) UNPUTCHR();
		}
		else PUTCHR(*ie++);
	}
						/*...INDENT*/
						if (op <= om) op = strcopy(op, "void");
						PUTCHR(')');
						if (flags & EXTERN)
						{
							PUTCHR(';');
							PUTCHR('\n');
							SYNCOUT();
							KEEPOUT();
						}
						else
						{
							PUTCHR('\n');
							PUTCHR(*ip);
						}
						ip++;
						flags &= ~(MATCH|SKIP);
					}
				}
#endif
				else if ((flags & (MATCH|PLUSONLY|SKIP|TOKENS)) == (MATCH|TOKENS))
				{
					line = proto->line;
					op = strcopy(om, " __PARAM__(");
					op = memcopy(op, im, ie - im);
					PUTCHR(',');
					PUTCHR(' ');
					PUTCHR('(');
					flags &= ~(MATCH|SKIP);
					if (flags & VARIADIC)
					{
						if ((vc = ie - im + 1) > sizeof(proto->variadic)) vc = sizeof(proto->variadic);
						memcopy(proto->variadic, im, vc);
						op = strcopy(op, "va_alist)) __OTORP__(va_dcl)\n{");
					}
					else
					{
						flags |= SKIP;
						proto->ip = im;
						proto->op = op;
						group = 0;
						brack = 0;
						for (;;)
						{
							switch (lex(proto, (flags & GLOBAL) | RECURSIVE))
							{
							case '[':
								brack++;
								continue;
							case ']':
								brack--;
								continue;
							case '(':
								if (paren++) group++;
								continue;
							case ')':
								if (--paren == 0)
								{
									group = 0;
									if (flags & MATCH)
									{
										flags &= ~(MATCH|SKIP);
										op = memcopy(op, m, e - m);
									}
									break;
								}
								continue;
							case ',':
								if (paren == 1)
								{
									group = 0;
									if (flags & MATCH)
									{
										flags &= ~(MATCH|SKIP);
										op = memcopy(op, m, e - m);
									}
									PUTCHR(',');
									PUTCHR(' ');
									proto->op = op;
								}
								continue;
							case T_ID:
								if (group <= 1 && !brack)
								{
									flags |= MATCH;
									m = proto->tp;
									e = proto->ip;
								}
								continue;
							default:
								continue;
							}
							break;
						}
						PUTCHR(')');
						PUTCHR(')');
					}
					if (!(flags & SKIP))
					{
						flags |= SKIP;
						proto->op = strcopy(op, " __OTORP__(");
						proto->ip = im + 1;
						n = *(ie - 1);
						*(ie - 1) = ';';
						c = *ie;
						*ie = 0;
						lex(proto, (flags & GLOBAL) | DECLARE);
						*(ie - 1) = n;
						*ie = c;
						proto->ip = ie;
						op = proto->op;
						PUTCHR(')');
					}
					if (flags & EXTERNALIZE) memcpy(proto->ox, "extern", 6);
					op = linesync(proto, op, proto->line = line);
					if (flags & DIRECTIVE)
					{
						proto->brace = 0;
						PUTCHR('\n');
						PUTCHR('#');
					}
					else if (!(flags & VARIADIC)) PUTCHR('{');
				}
			}
			flags &= ~(IDID|INDIRECT|MATCH|OTHER|SKIP);
			call = 0;
			group = 0;
			break;
		case '}':
			flags &= ~(IDID|INDIRECT|MATCH|OTHER|SKIP|TOKENS);
			if (--proto->brace == 0)
			{
				flags &= ~(INIT|VARIADIC|VARIADIC2);
#if PROTOMAIN
				if (flags & EXTERN) BACKOUT();
#endif
			}
			call = 0;
			group = 0;
			paren = 0;
			break;
		case '=':
			if (last == '?') flags |= DIRECTIVE;
			else if (paren == 0 && (flags & (INIT|MATCH|SKIP)) == MATCH)
			{
				if (last == ')' && proto->brace && (group != 2 || call != 2)) flags |= SKIP;
				else goto fsm_statement;
			}
			goto fsm_other;
		case ',':
#if PROTOMAIN
			if (flags & CLASSIC)
			{
				if (paren == 1) args++;
				else
				{
					args--;
					flags &= ~MATCH;
				}
				break;
			}
#endif
			if (paren == 0 && (flags & DECLARE)) *(op - 1) = c = ';';
			/*FALLTHROUGH*/
		case ';':
 fsm_statement:
			if (flags & INIT) /* ignore */;
#if PROTOMAIN
			else if (flags & CLASSIC)
			{
				if (paren == 0)
				{
					if ((flags & MATCH) && last == ')')
						flags &= ~MATCH;
					if (!(flags & MATCH))
					{
						call = 0;
						group = 0;
						flags &= ~SKIP;
						if (flags & EXTERN) BACKOUT();
						if (flags & SLIDE)
						{
							SYNC();
							return 0;
						}
					}
					else
					{
						args--;
						if ((flags & (EXTERN|SKIP)) == (EXTERN|SKIP))
							BACKOUT();
					}
				}
			}
#endif
			else if (paren == 0)
			{
				if ((flags & (MATCH|OTHER|SKIP)) == MATCH && call > 1)
				{
					if ((flags & MANGLE) && func)
					{
						func[0] = 'F';
						func[1] = 'U';
						func[2] = 'N';
						func[3] = 'C';
						func = 0;
					}
					if ((flags & (DECLARE|INDIRECT)) == INDIRECT && aim && aie < im)
					{
						while (aie < ip && (*aie == ' ' || *aie == '\t' || *aie == '\n')) aie++;
						v = aim;
						while (v < aie)
							if (*v++ == ')') break;
						while (v < aie && (*v == ' ' || *v == '\t' || *v == '\n')) v++;
						if (v == aie || !(flags & PLUSPLUS))
						{
							if (flags & PLUSPLUS) n = 3;
							else if (v == aie && *v == '(') n = 10;
							else n = 11;
							ko = op;
							om += n;
							v = op += n;
							while (v >= ko + n)
							{
								*v = *(v - n);
								v--;
							}
							if (flags & PLUSPLUS) memcopy(aom, "(...))", 6);
							else if (n == 10) memcopy(aom, "(__VARARG__))", 13);
							else
							{
								ko = strcopy(aom, " __PROTO__(");
								ko = memcopy(ko, aim, aie - aim);
								*ko = ')';
								if (++ko >= om)
								{
									*ko++ = ')';
									om = ko;
								}
							}
						}
					}
					else if (flags & TYPEDEF)
					{
						op = om;
						while (*--op == ' ' || *op == '\t' || *op == '\n');
						if (*op != ')')
						{
							op = om += 14;
							*--op = ')';
							while ((x = *(op - 14)) >= 'A' && x <= 'Z' || x >= 'a' && x <= 'z' || x >= '0' && x <= '9' || x == '_')
								*--op = x;
							memcopy(op - 13, "(__OTORP__(*)", 13);
						}
					}
					if (flags & OTHER)
						;
					else if (flags & PLUSPLUS)
					{
						op = om;
						if (!(flags & TOKENS)) op = strcopy(op, "(...)");
						else op = memcopy(op, im, ie - im);
						PUTCHR(c);
					}
					else
					{
						if (flags & DECLARE) op = strcopy(om, "()");
						else if (!(flags & TOKENS)) op = strcopy(om, "(__VARARG__)");
						else
						{
							op = strcopy(om, " __PROTO__(");
							op = memcopy(op, im, ie - im);
							PUTCHR(')');
						}
						if (flags & EXTERNALIZE) memcpy(proto->ox, "extern", 6);
						PUTCHR(c);
					}
					flags &= ~(MATCH|VARIADIC|VARIADIC2);
					if (c == ',' && !(flags & INDIRECT))
					{
						call = 1;
						group = 0;
						break;
					}
				}
				else if (flags & (OTHER|SKIP)) call = 0;
				if (c == ';')
				{
					flags &= ~(EXTERNALIZE|MANGLE|TOKENS|TYPEDEF);
					call = 0;
					if (flags & SLIDE)
					{
						SYNC();
						return 0;
					}
				}
				else call = call > 1 && c == ',';
				group = 0;
				flags &= ~(IDID|INDIRECT|MATCH|OTHER|SKIP);
			}
			else if (paren == 1 && group == 1 && !(flags & (IDID|MANGLE))) flags |= TOKENS|OTHER;
			break;
		case T_DO:
		case T_IF:
			flags |= TOKENS|SKIP;
			break;
		case T_EXTERN:
#if PROTOMAIN
			if (flags & CLASSIC)
			{
				if (proto->brace == 0)
					flags |= SKIP;
			}
			else
#endif
			if (paren == 0 && !(flags & TYPEDEF))
			{
				flags |= MANGLE;
				if (!(flags & PLUSONLY) || proto->package)
				{
					op = strcopy(op, " __MANGLE__");
					if (proto->package)
					{
						op = strcopy(op - 1, proto->package);
						func = op + 1;
						op = strcopy(op, "_DATA__");
					}
				}
				else
					func = 0;
			}
			break;
		case T_VARIADIC:
			if (paren == 0 && (flags & (DECLARE|VARIADIC)) == DECLARE)
			{
				op -= 3;
				SYNC();
				return c;
			}
			if (paren == 1 && !(flags & SKIP))
				flags |= VARIADIC;
			flags |= TOKENS;
			break;
		case T_VOID:
			goto fsm_id;
		case T_VA_START:
			if ((flags & (PLUSONLY|VARIADIC)) == VARIADIC)
			{
				flags &= ~MATCH;
				line = proto->line;
				op = strcopy(op - 8, "__VA_START__");
				SYNC();
				for (;;)
				{
					switch (lex(proto, (flags & GLOBAL) | RECURSIVE))
					{
					case 0:
					case ';':
						break;
					case T_ID:
						if (!(flags & MATCH))
						{
							flags |= MATCH;
							m = proto->tp;
							e = proto->ip;
						}
						continue;
					default:
						continue;
					}
					break;
				}
				CACHE();
				if (flags & MATCH)
				{
					v = m;
					n = e - m;
				}
				else
				{
					v = "ap";
					n = 2;
				}
				op = strcopy(op, " __OTORP__(");
				proto->ip = proto->variadic;
				proto->op = op;
				flags &= ~MATCH;
				group = 0;
				bp = proto->ip + 1;
				if (*bp == 'r' && !strncmp(bp, "register", 8) && (*(bp + 8) == ' ' || *(bp + 8) == '\t')) bp += 9;
				for (;;)
				{
					switch (lex(proto, (flags & GLOBAL) | RECURSIVE))
					{
					case '(':
						if (paren++) group++;
						continue;
					case ')':
						if (--paren == 0)
						{
							if (flags & MATCH)
							{
								flags &= ~MATCH;
								if (!(flags & VARIADIC2))
								{
									op = memcopy(op, m, e - m);
									op = strcopy(op, " = ");
								}
								op = strcopy(op, "va_arg(");
								op = memcopy(op, v, n);
								PUTCHR(',');
								PUTCHR(' ');
								if (m > bp) op = memcopy(op, bp, m - bp);
								else op = strcopy(op, "int ");
								if (group > 1) op = strcopy(op, ")()");
								else op = memcopy(op, e, proto->ip - e - 1);
								PUTCHR(')');
								PUTCHR(';');
							}
							group = 0;
							break;
						}
						continue;
					case ',':
						if (paren == 1)
						{
							if (flags & MATCH)
							{
								flags &= ~MATCH;
								if (!(flags & VARIADIC2))
								{
									op = memcopy(op, m, e - m);
									op = strcopy(op, " = ");
								}
								op = strcopy(op, "va_arg(");
								op = memcopy(op, v, n);
								PUTCHR(',');
								PUTCHR(' ');
								if (m > bp) op = memcopy(op, bp, m - bp);
								else op = strcopy(op, "int ");
								if (group > 1) op = strcopy(op, ")()");
								else op = memcopy(op, e, proto->ip - e - 1);
								PUTCHR(')');
								PUTCHR(';');
								bp = proto->ip + 1;
								if (*bp == 'r' && !strncmp(bp, "register", 8) && (*(bp + 8) == ' ' || *(bp + 8) == '\t')) bp += 9;
							}
							group = 0;
							proto->op = op;
						}
						continue;
					case T_ID:
						if (group <= 1)
						{
							flags |= MATCH;
							m = proto->tp;
							e = proto->ip;
						}
						continue;
					default:
						continue;
					}
					break;
				}
				op = strcopy(op, ")");
				flags |= VARIADIC2;
				proto->line = line;
				call = 0;
				break;
			}
			/*FALLTHROUGH*/
		case T_ID:
 fsm_id:
#if PROTOMAIN
			if (flags & CLASSIC)
			{
				if (!args && paren == 1) args++;
				break;
			}
#endif
			if (paren == 0)
			{
				if (last == ')')
				{
					if (proto->brace == 0 && !(flags & DECLARE)) flags |= SKIP;
					call = !call;
				}
				else if ((flags & SKIP) || c == T_ID || c == T_VOID) call++;
				else flags |= SKIP;
				if (last == T_ID) flags |= IDID;
			}
			c = T_ID;
			flags |= TOKENS;
			break;
		case T_INVALID:
			if (*proto->tp >= '0' && *proto->tp <= '9')
			{
				n = 0;
				for (;; op--)
				{
					switch (*(op - 1))
					{
					case 'f':
					case 'F':
						t = op;
						while ((c = *--t) >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
						if (*t == '.')
							op--;
						n = 0;
						break;
					case 'l':
					case 'L':
						if (!(n & 01))
						{
							n |= 01;
							t = op;
							while ((c = *--t) >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
							if (*t == '.')
							{
								n = 0;
								op--;
								break;
							}
						}
						continue;
					case 'u':
					case 'U':
						n |= 02;
						continue;
					}
					break;
				}
				if (n & 01)
					*op++ = 'L';
				if (n & 02)
				{
					m = op;
					t = op = m + 10;
					while ((c = *--m) >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')
						*--t = c;
					c = *t;
					strcopy(m + 1, "(unsigned)");
					*t = c;
					break;
				}
			}
			goto fsm_other;
#if PROTOMAIN
		case '[':
			if ((flags & CLASSIC) && paren == 0 && group <= 2) flags |= SKIP;
			/*FALLTHROUGH*/
#endif
		default:
 fsm_other:
#if PROTOMAIN
			if (flags & CLASSIC) break;
#endif
			flags |= TOKENS;
			if (paren == 0) flags |= OTHER;
			break;
		}
		else if (c == '#' && *ip != '(') flags |= SHARP;
		last = c;
#if PROTOMAIN
		if ((flags & (EXTERN|MATCH)) == (EXTERN|MATCH) && ((flags & (DIRECTIVE|SKIP)) || proto->brace || c != '(' && c != ')' && c != '*' && c != T_ID))
			CACHEOUT();
		else
#endif
		SYNCOUT();
		goto fsm_start;
	}
	else if (flags & (INIT_DEFINE|INIT_INCLUDE))
	{
#if PROTOMAIN
		if ((flags & YACC) && c == '%' && *ip == '{')
			t = 0;
		else
#endif
		{
			if (c == '#')
			{
				for (t = ip; *t == ' ' || *t == '\t'; t++);
				if (*t++ == 'i' && *t++ == 'f' && *t++ == 'n' && *t++ == 'd' && *t++ == 'e' && *t++ == 'f')
				{
#if !PROTOMAIN
					while (*t == ' ' || *t == '\t') t++;
					if (*t != '_')
#endif
						t = 0;
				}
			}
			else
				t = "";
		}
		if (t)
		{
#if PROTOMAIN
			n = ip - proto->tp;
			ip -= n;
			op -= n;
#else
			ip = bp;
			op = proto->op;
#endif
		}
		else
			while (*ip != '\n')
				*op++ = *ip++;
		op = init(proto, op, flags);
		op = linesync(proto, op, proto->line);
		flags &= ~(INIT_DEFINE|INIT_INCLUDE);
		proto->flags &= ~(INIT_DEFINE|INIT_INCLUDE);
		goto fsm_start;
	}
	SYNC();
	return c;
}

/*
 * close a proto buffer stream
 */

void
pppclose(char* iob)
{
	register Proto_t*	proto = (Proto_t*)(iob - sizeof(Proto_t));

	if (proto->flags & MORE) close(proto->fd);
	free((char*)proto); /* some ANSI cc's botch the free() prototype */
}

/*
 * open a new proto buffer stream
 * read buffer pointer returned
 * 0 returned on error or if no magic
 *
 *	file	!=0	file path to open, otherwise use fd
 *	fd		open file fd if file==0
 *	notice	!=0	copyright notice info commented at the top
 *	options	!=0	additional notice name=value pairs, space or ; separated
 *	package	!=0	generate header for this package
 */

char*
pppopen(char* file, int fd, char* notice, char* options, char* package, char* comment, int flags)
{
	register Proto_t*	proto;
	register char*		iob;
	register long		n;
	register char*		s;
	char*			t;
	int			pragma;
	int			clr;
	int			hit;
	int			i;
	int			z;
	char*			b;
#if PROTOMAIN
	int			comlen;
	char			com[80];
#endif
	int			m = 0;

	static int		retain;

	/*
	 * initialize proto
	 */

#if PROTOMAIN
	if (flags & PROTO_CLASSIC) flags &= ~PROTO_INCLUDE;
#endif
	if (flags & PROTO_RETAIN) flags &= ~retain;
	else retain &= PROTO_INITIALIZED;
	if (file && (fd = open(file, O_RDONLY)) < 0) return 0;
#if !PROTOMAIN
	if ((n = lseek(fd, 0L, 2)) > 0)
	{
		if (lseek(fd, 0L, 0)) return 0;
		if (n < CHUNK) n = CHUNK;
		else if (n > 2 * BLOCK) n = 0;
		m = 1;
	}
	if (n > 0)
	{
		/*
		 * file read in one chunk
		 */

		if (!(proto = newof(0, Proto_t, 1, 4 * n + 2)))
			return 0;
		proto->iz = n;
		proto->oz = 3 * n;
		n = 0;
	}
	else
#endif
	{
		/*
		 * file read in BLOCK chunks
		 */

		n = BLOCK;
		if (!(proto = newof(0, Proto_t, 1, 5 * n + 2)))
			return 0;
		proto->iz = n;
		proto->oz = 3 * n;
		proto->flags |= MORE;
	}
	proto->fd = fd;
	proto->package = package;
	iob = (char*)proto + sizeof(Proto_t);
	proto->op = proto->ob = iob;
	proto->ip = proto->ib = iob + proto->oz + n;
	if (m) proto->options |= REGULAR;
	if (!comment)
		comment = "/*";
	if (!(proto->cc[0] = comment[0]))
		notice = options = 0;
	else if (comment[1])
	{
		proto->cc[1] = comment[1];
		proto->cc[2] = comment[2] ? comment[2] : comment[0];
	}
	else
		proto->cc[1] = proto->cc[2] = comment[0];

	/*
	 * read the first chunk
	 */

	n = read(fd, proto->ip, proto->iz);
	if (!(proto->flags & MORE))
		close(fd);
	if (n < 0)
	{
		pppclose(iob);
		return 0;
	}
	*(proto->ip + n) = 0;

	/*
	 * check for proto pragma in first block of lines
	 * pragma blanked out if found
	 *
	 *	-1	no pragma
	 *	 0	#pragma noprototyped
	 *	 1	#pragma prototyped
	 *
	 * NOTE: matches may occur inside comments and quotes
	 */

#if PROTOMAIN
	if (!notice && !options || (comlen = astlicense(com, sizeof(com), NiL, "type=check", proto->cc[0], proto->cc[1], proto->cc[2])) <= 0)
		*com = 0;
#endif
	hit = (notice || options) ? 0 : HIT_noticed;
	pragma = -1;
	s = proto->ip;
	m = MAGICTOP;
	while (m-- > 0 && *s && hit != (HIT_prototyped|HIT_noticed))
	{
		while (*s == ' ' || *s == '\t')
			s++;
		if (*s == '#')
		{
			b = s++;
			while (*s == ' ' || *s == '\t')
				s++;
			if (*s == *PRAGMADIR && !strncmp(s, PRAGMADIR, sizeof(PRAGMADIR) - 1) && (*(s += sizeof(PRAGMADIR) - 1) == ' ' || *s == '\t'))
			{
				clr = 0;
				while (*s && *s != '\r' && *s != '\n')
				{
					for (; *s == ' ' || *s == '\t'; s++);
					for (t = s; *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n'; s++);
					z = s - t;
					for (i = 0; i < elementsof(pragmas); i++)
						if (pragmas[i].size == z && !strncmp(t, pragmas[i].name, z))
						{
							clr = 1;
							hit |= pragmas[i].hit;
							switch (pragmas[i].hit)
							{
							case HIT_noticed:
								notice = options = 0;
								break;
							case HIT_prototyped:
								pragma = pragmas[i].val;
								break;
							}
						}
				}
				if (clr)
				{
#if PROTOMAIN
					if (!(flags & PROTO_DISABLE) || (flags & PROTO_NOPRAGMA))
#endif
					for (; b < s; *b++ = ' ');
				}
			}
		}
		else if (*s == *GENERATED && !strncmp(s, GENERATED, sizeof(GENERATED) - 1))
		{
			pragma = 0;
			break;
		}
#if PROTOMAIN
		else if (*s == '%' && *(s + 1) == '{')
			proto->flags |= YACC;
		else if (!(hit & HIT_noticed))
		{
			if (*s == *com && !strncmp(s, com, comlen))
			{
				hit |= HIT_noticed;
				notice = options = 0;
			}
			else
				for (; *s && *s != '\n' && !(hit & HIT_noticed); s++)
					for (i = 0; i < elementsof(notices); i++)
						if (*s == notices[i].name[0] && !strncmp(s, notices[i].name, notices[i].size))
						{
							s += notices[i].size;
							if (notices[i].val)
							{
								while (*s == ' ' || *s == '\t')
									s++;
								if (*s == '(' && (*(s + 1) == 'c' || *(s + 1) == 'C') && *(s + 2) == ')' || *s >= '0' && *s <= '9' && *(s + 1) >= '0' && *(s + 1) <= '9')
								{
									hit |= notices[i].hit;
									notice = options = 0;
								}
							}
							else
							{
								hit |= notices[i].hit;
								notice = options = 0;
							}
							break;
						}
		}
#endif
		while (*s && *s++ != '\n');
	}
	if (flags & PROTO_PLUSPLUS) proto->flags |= PLUSPLUS;
	if (flags & PROTO_TEST) proto->test = 1;
	if (flags & PROTO_EXTERNALIZE) proto->options |= EXTERNALIZE;
#if PROTOMAIN
	if (flags & PROTO_CLASSIC) pragma = -pragma;
	if (flags & PROTO_DISABLE) pragma = 0;
	if (flags & PROTO_LINESYNC) proto->flags |= LINESYNC;
	if (!(proto->flags & YACC) && file && (m = strlen(file)) > 2 && file[--m] == 'y' && file[--m] == '.')
		proto->flags |= YACC;
#endif
	if (pragma <= 0)
	{
		if (flags & PROTO_PLUSPLUS)
		{
			flags &= ~(PROTO_HEADER|PROTO_INCLUDE);
			proto->flags |= PLUSONLY;
		}
		else if (!(flags & (PROTO_FORCE|PROTO_PASS)))
		{
			pppclose(iob);
			return 0;
		}
		else if ((flags & (PROTO_FORCE|PROTO_PASS)) == PROTO_PASS || !pragma)
		{
			proto->flags |= PASS;
			if (proto->flags & MORE)
				proto->oz += proto->iz;
			proto->iz = n;
			if (notice || options)
			{
				if (proto->cc[0] == '#' && proto->ip[0] == '#' && proto->ip[1] == '!')
				{
					s = proto->ip;
					while (*s && *s++ != '\n');
					m = s - proto->ip;
					proto->op = memcopy(proto->op, proto->ip, m);
					proto->ip = s;
					proto->iz = n -= m;
				}
#if PROTOMAIN
				if (proto->cc[0])
				{
					if ((comlen = astlicense(proto->op, proto->oz, notice, options, proto->cc[0], proto->cc[1], proto->cc[2])) < 0)
						proto_error((char*)proto + sizeof(Proto_t), 1, proto->op, NiL);
					else
						proto->op += comlen;
				}
				if (!(flags & PROTO_CLASSIC) && !(proto->flags & YACC))
#endif
				proto->op = linesync(proto, proto->op, 1);
				proto->iz += proto->op - proto->ob;
			}
			memcopy(proto->op, proto->ip, n);
			return iob;
		}
	}
#if PROTOMAIN
	if (!(retain & PROTO_INITIALIZED))
	{
		retain |= PROTO_INITIALIZED;
		ppfsm(FSM_INIT, NiL);
	}
#endif
	proto->line = 1;
#if CHUNK >= 512
	if (notice || options || (flags & (PROTO_HEADER|PROTO_INCLUDE)))
	{
#if PROTOMAIN
		if (notice || options)
		{
			if ((comlen = astlicense(proto->op, proto->oz, notice, options, proto->cc[0], proto->cc[1], proto->cc[2])) < 0)
				proto_error((char*)proto + sizeof(Proto_t), 1, proto->op, NiL);
			else
				proto->op += comlen;
		}
#endif
		if (flags & PROTO_INCLUDE)
		{
			proto->flags |= INIT_INCLUDE;
			if (flags & PROTO_RETAIN)
				retain |= PROTO_INCLUDE;
		}
		else if (flags & PROTO_HEADER)
		{
			if (flags & PROTO_RETAIN) retain |= PROTO_HEADER;
#if PROTOMAIN
			if (flags & PROTO_CLASSIC)
			{
				*proto->op++ = '#';
				proto->op = strcopy(proto->op, PRAGMADIR);
				*proto->op++ = ' ';
				proto->op = strcopy(proto->op, pragmas[0].name);
				*proto->op++ = '\n';
			}
			else
#endif
			proto->flags |= INIT_DEFINE;
		}
#if PROTOMAIN
		if (!(flags & PROTO_CLASSIC))
		{
			if (proto->flags & YACC)
			{
				proto->op = strcopy(proto->op, "\n%{\n" + !notice);
				proto->op = strcopy(proto->op, GENERATED);
				proto->op = strcopy(proto->op, "%}\n");
			}
			else
			{
				if (n || notice || options)
					*proto->op++ = '\n';
				proto->op = strcopy(proto->op, GENERATED);
				if (n)
					proto->op = linesync(proto, proto->op, proto->line);
				else if (proto->flags & (INIT_DEFINE|INIT_INCLUDE))
					proto->op = init(proto, proto->op, proto->flags);
			}
		}
#endif
	}
#endif
#if PROTOMAIN
	proto->file = file;
	if (flags & PROTO_CLASSIC)
	{
		proto->flags |= CLASSIC;
		if (!(flags & PROTO_HEADER)) proto->flags |= EXTERN;
	}
#endif
	return iob;
}

/*
 * read next proto'd chunk into iob
 * the chunk is 0 terminated and its size is returned
 */

int
pppread(char* iob)
{
	register Proto_t*	proto = (Proto_t*)(iob - sizeof(Proto_t));
	register int		n;

	if (proto->flags & PASS)
	{
		if (proto->iz)
		{
			n = proto->iz;
			proto->iz = 0;
		}
		else if (!(proto->flags & MORE)) n = 0;
		else if ((n = read(proto->fd, proto->ob, proto->oz)) <= 0 || (proto->options & REGULAR) && n < proto->oz)
		{
			proto->flags &= ~MORE;
			close(proto->fd);
		}
	}
	else
	{
		if (proto->op == proto->ob)
		{
			if (proto->flags & ERROR) return -1;
#if PROTOMAIN
			if (proto->flags & YACC)
			{
				register char*	ip = proto->ip;
				register char*	op = proto->ob;
				register char*	ep = proto->ob + proto->oz - 2;

				if (!*ip)
				{
					ip = proto->ip = proto->ib;
					if (!(proto->flags & MORE)) n = 0;
					else if ((n = read(proto->fd, ip, proto->iz)) <= 0 || (proto->options & REGULAR) && n < proto->iz)
					{
						if (n < 0) n = 0;
						proto->flags &= ~MORE;
						close(proto->fd);
					}
					ip[n] = 0;
				}
				if (proto->flags & YACCSPLIT)
				{
					proto->flags &= ~YACCSPLIT;
					if (*ip == '%')
					{
						*op++ = *ip++;
						if (proto->flags & YACC2) proto->flags &= ~YACC;
						else proto->flags |= YACC2;
					}
				}
				if (proto->flags & YACC)
					while (op < ep && (n = *op++ = *ip))
					{
						ip++;
						if (n == '%')
						{
							if (*ip == '%' && (ip == proto->ip + 1 || *(ip - 2) == '\n'))
							{
								*op++ = *ip++;
								if (proto->flags & YACC2) proto->flags &= ~YACC;
								else proto->flags |= YACC2;
								break;
							}
							if (!*ip)
							{
								*op++ = '%';
								proto->flags |= YACCSPLIT;
								break;
							}
						}
						else if (n == '\n') proto->line++;
					}
				proto->op = memcopy(proto->ob, proto->ip, ip - proto->ip);
				proto->ip = ip;
			}
			else
#endif
			lex(proto, proto->flags);
			if ((proto->flags & (ERROR|MORE)) == ERROR)
				proto->op = strcopy(proto->op, "/* NOTE: some constructs may not have been converted */\n");
		}
		n = proto->op - proto->ob;
		proto->op = proto->ob;
	}
	return n;
}

#if !PROTOMAIN

/*
 * drop control of iob after first pppread()
 * return value is input fd
 * if fd<0 then all data in iob
 */

int
pppdrop(char* iob)
{
	register Proto_t*	proto = (Proto_t*)(iob - sizeof(Proto_t));

	if (proto->flags & MORE)
	{
		proto->flags &= ~MORE;
		return proto->fd;
	}
	return -1;
}

#endif
